<template>
  <div class="tree-container">
    <div
      v-for="item in selfTreeData"
      :key="item[options.id] + item[options.nodeName]"
    >
      <div class="tree-node">
        <!-- 第一级不需要缩进 -->
        <span
          v-if="level > 0"
          :style="{ width: (level) * 24 + 'px' }"
          class="space"
        />
        <!-- 不展开 -->
        <img
          v-if="showId != item[options.id] && item[options.children] && item[options.children].length > 0"
          src="../assets/Polygon3.png"
          class="relevance"
          alt=""
        >
        <!-- 展开 -->
        <img
          v-if="showId == item[options.id] && item[options.children] && item[options.children].length > 0"
          src="../assets//Polygon1.png"
          class="relevance2"
          alt=""
        >
        <!-- 非叶子节点 -->
        <span
          @click="clickTreeNode(item)"
          v-if="item[options.children] && item[options.children].length > 0"
          :style="{ width: `calc(100% - 29px - ${(level - 1) * 24}px)` }"
          :class="item.checked == 1 ? 'is-active' : ''"
        >{{
          item[options.nodeName] }} </span>
        <!-- 叶子节点 -->
        <span
          v-else
          :style="{ width: `calc(100% - 29px - ${(level - 1) * 24}px)` }"
          :class="item.checked == 1 ? 'is-active' : ''"
        >{{ item[options.leafName] }} </span>
        <span
          class="check-box"
          @click="changeChecked(item)"
        >
          <span
            class="noChecked"
            v-show="!item.checked"
          />
          <span
            class="indeterminate"
            v-show="item.checked == 0.5"
          />
          <span
            class="hasChecked"
            v-show="item.checked == 1"
          />
        </span>
      </div>
      <div v-show="showId == item[options.id]">
        <myTree
          v-if="item[options.children] && item[options.children].length"
          :tree-data="item[options.children]"
          :level="level + 1"
          :ref="`chTree${item[options.id]}`"
          @changeParent="changeParent"
          @clearUnlimited="clearUnlimited"
          :options="options"
        />
      </div>
    </div>
  </div>
</template>

<script>

export default {
  name: 'MyTree',
  props: {
    treeData: {
      required: true,
      type: Array
    },
    level: {
      type: Number,
      default: 0
    },
    options: {
      type: Object,
      default: () => ({
        id: 'code',
        parentId: 'parentCode',
        children: 'childList',
        nodeName: 'name',
        leafName: 'checkContent'
      })
    }
  },
  data() {
    return {
      selfTreeData: [],
      showChildren: true,
      showId: '',
      parentState: 0.5
    }
  },
  watch: {
    treeData: {
      handler(val) {
        this.selfTreeData = val;
      },
      immediate: true,
      deep: true
    }
  },
  created() {
  },
  methods: {
    clickTreeNode(item) {
      if (this.showId == item[this.options.id]) {
        this.showId = ''
      } else {
        this.showId = item[this.options.id]
      }
    },
    changeChecked(treeNode) {
      if (!treeNode[this.options.id]) {
        // 选中不限，需要清除下面的所有
        if (!treeNode.checked) { return this.clearChecked(); }
      } else if (this.level) {
        // 清除code为null的
        this.$emit('clearUnlimited')
      } else {
        // 清除code为null的
        this.clearUnlimited()
      }
      // checked 被改变
      if (!treeNode.checked) return this.handleChangeNodeChecked(treeNode, 1);
      if (treeNode.checked == this.parentState) return this.handleChangeNodeChecked(treeNode, 1);
      if (treeNode.checked == 1) return this.handleChangeNodeChecked(treeNode, 0);
    },
    handleChangeNodeChecked(treeNode, state, isClear = false) {
      // this.$set(treeNode, 'checked', state);
      treeNode.checked=state
      // 通知父级，改变父级选中状态。
      if (!isClear) { this.$emit('changeParent', treeNode[this.options.parentId]); }
      // 将子级全部选中。
      if (treeNode[this.options.id] && this.$refs[`chTree${treeNode[this.options.id]}`] && this.$refs[`chTree${treeNode[this.options.id]}`][0]) { this.$refs[`chTree${treeNode[this.options.id]}`][0].changeChChecked(treeNode, state); }
    },
    changeParent(parentCode) {
      // 子级发生改变，更新父级选中状态。
      const pNode = this.selfTreeData.find(node => node[this.options.id] === parentCode);
      if (!parentCode) return;
      // 父级肯定有childList
      let state = 0, hasChecked = false, hasNoChecked = false;
      // 此处不统计个数是假设数组很长，我前两个就能判断的话减少性能消耗。
      pNode[this.options.children].some((node, index) => {
        if (node.checked == this.parentState) {
          // 直接将当前设置为半选
          state = this.parentState;
          return true;
        }
        // 只有存在又有选中，又有未选中则直接设置为0.5
        if (node.checked == 1) {
          hasChecked = true;
        }
        if (!node.checked) {
          hasNoChecked = true;
        }
        if (hasChecked && hasNoChecked) {
          state = this.parentState;
          return true;
        }
      })
      // 此处只要判断state是否还是0，如果还是0且hasChecked为true证明需要为1。
      state = state === 0 && hasChecked ? 1 : state;
      // this.$set(pNode, 'checked', state);
      pNode.checked=state
      this.$emit('changeParent', pNode[this.options.parentId]);
    },
    changeChChecked(treeNode, state) {
      // 改变子级的选中状态
      if (treeNode[this.options.children] && treeNode[this.options.children].length) {
        treeNode[this.options.children].forEach(node => {
          // this.$set(node, 'checked', state);
         node.checked=state
          if (this.$refs[`chTree${node[this.options.id]}`] && this.$refs[`chTree${node[this.options.id]}`][0]) { this.$refs[`chTree${node[this.options.id]}`][0].changeChChecked(node, state); }
        })
      }
    },
    clearUnlimited() {
      // 清除不限
      if (this.level) {
        this.$emit('clearUnlimited')
      } else {
        const unlimitedNode = this.selfTreeData.find(node => !node[this.options.id]);
        if (unlimitedNode && unlimitedNode.checked) {
          unlimitedNode.checked = 0;
        }
      }
    },
    getChecked() {
      const checkedCodes = [];
      this.selfTreeData.forEach(node => {
        const checked = node.checked;
        if (checked == 1 || checked == this.parentState) {
          checkedCodes.push(node);
        }
        if (checked && this.$refs[`chTree${node[this.options.id]}`] && this.$refs[`chTree${node[this.options.id]}`][0]) { checkedCodes.push(...this.$refs[`chTree${node[this.options.id]}`][0].getChecked()); }
      })
      return checkedCodes;
    },
    clearChecked() {
      // 业务需求，重置的时候勾选不限。
      this.selfTreeData.forEach(node => {
        if (node[this.options.id]) {
          this.handleChangeNodeChecked(node, 0, true)
        } else { 
          // this.$set(node, 'checked', 1) 
          node.checked=1
        }
      })
    },
    changeCheckedById(id, state) {
      this.selfTreeData.forEach(node => {
        if (node[this.options.id] == id) {
          // this.$set(node, 'checked', state);
          node.checked=state
          // 通知父级，改变父级选中状态。
          this.$emit('changeParent', node[this.options.parentId]);
        }
        if (this.$refs[`chTree${node[this.options.id]}`] && this.$refs[`chTree${node[this.options.id]}`][0]) {
          this.$refs[`chTree${node[this.options.id]}`][0].changeCheckedById(id, state)
        }
      })
    }
  }
}
</script>

<style lang="scss" scoped>
.tree-node {

  padding: 26px 70px 26px 57px;
  color: #262626;
  font-size: 28px;
  box-shadow: inset 0px -1px 0px 0px #E6E6E6;
  text-align: left;
  box-sizing: border-box;
  position: relative;
  display: flex;

  .space {
    height: 1px;
    display: inline-block;
  }

  .relevance {
    height: 16px;
    width: 12px;
    display: inline-block;
    margin-right: 14px;
    margin-top: 5px;
  }

  .relevance2 {
    height: 12px;
    width: 16px;
    display: inline-block;
    margin-right: 14px;
    margin-top: 5px;
  }

  .check-box {
    position: absolute;
    right: 24px;
    overflow: auto;

    img {
      height: 40px;
      width: 40px;
    }
  }

  .hasChecked {
    display: inline-block;
    position: relative;
    border: 1px solid #dcdfe6;
    border-radius: 2px;
    box-sizing: border-box;
    width: 30px;
    height: 30px;
    transition: border-color .25s cubic-bezier(.71, -.46, .29, 1.46), background-color .25s cubic-bezier(.71, -.46, .29, 1.46);
    background-color: rgba(1, 113, 253, 1);
    border-color: rgba(1, 113, 253, 1);

    &:after {
      box-sizing: content-box;
      content: "";
      border: 5px solid #fff;
      border-left: 0;
      border-top: 0;
      height: 15px;
      left: 8px;
      position: absolute;
      top: 1px;
      width: 7px;
      transition: transform .15s ease-in .05s;
      transform-origin: center;
      transform: rotate(45deg) scaleY(1);
    }
  }

  .noChecked {
    width: 30px;
    height: 30px;
    border: rgba(119, 119, 119, 1) 1px solid;
    background-color: #fff;
    display: inline-block;
    box-sizing: border-box;
  }

  .indeterminate {
    display: inline-block;
    position: relative;
    border: 1px solid #dcdfe6;
    border-radius: 2px;
    box-sizing: border-box;
    width: 30px;
    height: 30px;
    transition: border-color .25s cubic-bezier(.71, -.46, .29, 1.46), background-color .25s cubic-bezier(.71, -.46, .29, 1.46);
    background-color: rgba(1, 113, 253, 1);
    border-color: rgba(1, 113, 253, 1);

    &:before {
      content: "";
      position: absolute;
      display: block;
      background-color: #fff;
      height: 5px;
      transform: scale(.5);
      left: 0;
      right: 0;
      top: 12px;
    }
  }

  .is-active {
    color: rgba(1, 113, 253, 1);
  }
}
</style>
